--[[
    author:ZYB
    time:2021-10-28 11:29:08
]]
local require = require;
local ngx = ngx;
local pairs = pairs
local type = type;
local string = string;
local tostring = tostring;
local table = table;
local tonumber = tonumber;
local ipairs =ipairs
local uu = require("modules.commhm.utils");
local log = require("modules.commhm.log");
local comm_checker = require("modules.commhm.auth_comm_checker")

local FOO = {
    _VERSION = '0.12.1',

    -- 加密校验版本
    ENCRYPT = {
        MD5_OLD  = 0,     -- 旧的md5加密方式，默认走这个
        MD5_VER1 = 1,     -- 在需要加密参数列表里的参数，都需要参与加密
        MD5_VER2 = 2,     -- 在排除参数列表里的参数不加密，其余参数都需要加密
        MD5_VER3 = 3,     -- ver3, 在ver2 版本的基础上，加了登录态校验
    }
};

local g_enum = {
    -- MD5加密
    md5_private_key = {
        act = "3dbc5f33add11d1af78ba2af365e0952",
        cmd = "ebd733cd91fcef8cf0d46efefa728348"
    },

    -- MD5检查参数列表
    md5_check_param_list = {
                --  必带参数
        act = 1,
        time = 1,
        apiid = 1,
        ver = 1,
        uin = 1,
        s2t = 1,
        -- 通用参数 通用的名字方便其他功能直接使用
        param_id = 1,
        param_num = 1,
        -- 自定义参数
        type = 1,
        num = 1,
        buy_times = 1,
        times = 1,
        gift_id = 1,
        item_id = 1,
        buy_id = 1,
        encrypt_ver = 1,
        itemnum = 1,
        production_id = 1,
        taskidx = 1,
        skinid = 1,
        headIndex = 1,
        status = 1,
        content = 1,
        platform = 1,
        itemid = 1,
        fee_type = 1,
        value = 1,
        id = 1,
        auth = 1,
        friend_uin = 1,
        fee_id = 1,
        target = 1,
        title = 1,
        iswarehouse = 1,
        avatarUrl = 1,
        coin_num = 1,
        op_ret = 1,
        event = 1,
        prices_id = 1,
        nickname = 1,
        awardid = 1,
        price = 1,
        ishide = 1,
        cost = 1,
        ad_id = 1,
        survival = 1,
        pricetype = 1,
        itemtype = 1,
        position_id = 1,
        platform_id = 1,
    },

    --MD5 v2版本排除加密的字段
    md5_exclude_list = {
        log = 1,
        test = 1,
        json = 1,
        nickname = 1,
        title = 1,
        content = 1,
        md5 = 1,
        auth = 1,
        content_ctx = 1,
    },

    -- MD5
}

local check_md5_param = g_enum.md5_check_param_list

-- 检查命令数据的合法性
FOO.check_uri_args = function()
    local private_key = g_enum.md5_private_key.act
    local param_list = {}
    for k, _ in pairs(ngx.ctx.m_params or {}) do
        if check_md5_param[k] then param_list[#param_list+1] = k end
    end

    -- 验证签名
    local auth_md5 = FOO.make_md5(ngx.ctx.m_params, param_list, private_key)
    if not auth_md5 then
        -- 参数不存在
        log.day_list("param_check", "not auth_md5 error")
        return false
    end
    local auth_ = ngx.ctx.m_params.md5 or ngx.ctx.m_params.auth
    if auth_md5 ~= (ngx.ctx.m_params.md5 or "") then
        -- 验签失败
        log.day_list("param_check", uu.to_str(param_list), uu.to_str(auth_md5),
                     "|", uu.to_str(auth_),
                     "|", uu.to_str(ngx.ctx.m_params.uin))
        return false
    end

    return true
end

FOO.check_uri_args_v2 = function()
    -- 验证签名
    local auth_md5, param_list = FOO.make_md5_v2(ngx.ctx.m_params)
    if not auth_md5 then
        -- 参数不存在
        log.day_list("param_check", "not auth_md5 error")
        return false
    end
    local auth_ = ngx.ctx.m_params.md5 or ngx.ctx.m_params.auth
    if auth_md5 ~= (auth_ or "") then
        -- 验签失败
        log.day_list("param_check", uu.to_str(param_list), uu.to_str(auth_md5),
                     "|", uu.to_str(auth_),
                     "|", uu.to_str(ngx.ctx.m_params.uin))
        return false
    end

    return true
end

FOO.make_md5_v2 = function( tparams )
    local private_key = g_enum.md5_private_key.act
    local param_list = {}
    for k, _ in pairs(tparams or {}) do
        if not g_enum.md5_exclude_list[k] then
            param_list[#param_list+1] = k
        end
    end

    -- 验证签名
    local auth_md5 = FOO.make_md5(tparams, param_list, private_key)
    return auth_md5, param_list
end

FOO.make_md5 = function(request_list, param_list, private_key)
    table.sort(param_list, function(lh, rh) if lh < rh then return true end end)

    local base_str = FOO.pack_base_str(request_list, param_list)
    if not base_str then return false end

    local auth_md5 = ngx.md5(base_str .. private_key)
    return auth_md5
end

FOO.pack_base_str = function(request_list, param_list)
    local md5_table = {}
    for _, k in ipairs(param_list) do
        local v = ngx.escape_uri(tostring(request_list[k]))
        if v then
            md5_table[#md5_table+1] = string.format("%s=%s", k, v)
        end
    end
    return table.concat(md5_table, '&')
end

FOO.check_token = function (auth_code)
    local auth_ = ngx.ctx.m_params.md5 or ngx.ctx.m_params.auth
    local time_ = ngx.ctx.m_params.time
    local uin   = ngx.ctx.m_params.uin

    if not auth_ or not time_ then
        return false, 'params error'
    end

    local md5 = ""
    if ngx.ctx.m_params.act then
        md5 = ngx.md5(time_ .. comm_checker.genS2Salt() .. uin) --out
    else
        auth_code = auth_code or "#miniw_907#"
        md5 = ngx.md5(time_ .. auth_code .. uin) --in
    end
    if md5 == string.lower(auth_) then
        return true
    else
        return false
    end
end

FOO.check_uri_args_v3 = function()
    -- 验证签名
    local s2 = ngx.ctx.m_params.s2
    local auth_md5, param_list = FOO.make_md5_v3(ngx.ctx.m_params)
    if not auth_md5 then
        -- 参数不存在
        log.day_list("param_check", "not auth_md5 error")
        return false
    end
    local auth_ = ngx.ctx.m_params.md5 or ngx.ctx.m_params.auth
    if auth_md5 ~= (auth_ or "") then
        -- 验签失败
        log.day_list("param_check", uu.to_str(ngx.ctx.m_params), uu.to_str(auth_md5),
                     "|", uu.to_str(auth_),
                     "|", uu.to_str(ngx.ctx.m_params.uin),
                     "|", tostring(s2), tostring(comm_checker.genS2Salt())
                    )
        return false
    end

    return true
end

FOO.make_md5_v3 = function( tparams )
    local private_key = g_enum.md5_private_key.act
    tparams.s2 = comm_checker.genS2Salt()
    local param_list = {}
    for k, _ in pairs(tparams or {}) do
        if not g_enum.md5_exclude_list[k] then
            param_list[#param_list+1] = k
        end
    end

    -- 验证签名
    local auth_md5 = FOO.make_md5(tparams, param_list, private_key)
    return auth_md5, param_list
end

-- !@ version_min - 最低版本限制
FOO.check_by_version = function( version_min )
    local encrypt_ver = tonumber(ngx.ctx.m_params.encrypt_ver) or 0

    -- 最低版本限制
    version_min = version_min or FOO.ENCRYPT.MD5_OLD
    if encrypt_ver < version_min then
        return false, 'encrypt version limit'
    end

    return ((({
        [FOO.ENCRYPT.MD5_OLD]  = FOO.check_token,
        [FOO.ENCRYPT.MD5_VER1] = FOO.check_uri_args,
        [FOO.ENCRYPT.MD5_VER2] = FOO.check_uri_args_v2,
        [FOO.ENCRYPT.MD5_VER3] = FOO.check_uri_args_v3,
    })[encrypt_ver]) or function() return false end)()
end

return FOO
